/*
 * @Author: Drc
 * @Date: 2020-06-11 18:39:14
 * @LastEditors: Drc
 * @LastEditTime: 2021-12-22 17:25:18
 */

import axios, {AxiosRequestConfig, AxiosResponse} from 'axios'
import Token from '@path/config/token'
import Config from '@path/config/index'
import store from '@path/redux'
import NavigationController from '@path/navigation/controller'
import Locale from '@path/locale'
import AsyncStorage from '@react-native-community/async-storage'
import CryptoJS, {MD5} from 'crypto-js'

enum Language {
  'zh' = 0,
  'en' = 1,
}

let last401: number

const instance = axios.create({
  baseURL: Config.API_HOST,
  timeout: 20000,
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    timestamp: new Date().getTime(),
  },
})

// // axios拦截器
instance.interceptors.request.use(async (config) => {
  const token = await Token.get()

  if (token) {
    config.headers.authorization = token
  }
  //拦截器处理
  if (config.method === 'get') {
    config.params = {
      ...config.data,
      _t: new Date().getTime() / 1000,
    }
  }
  config.headers['Accept-Language'] = Language[Locale.currentLocale]
  console.log(logRequest(config))
  return config
})

instance.interceptors.response.use(
  (response) => {
    //请求返回数据处理
    console.log(logResponse(response))
    if (response.data?.code === 401) {
      //频繁401不自动退出
      let current = new Date().getTime()
      if (last401 && current - last401 < 5000) {
        return
      }
      last401 = current
      store.dispatch.utils.showToast({text: Locale.getString('common.login_timeout')})
      store.dispatch.user.logout()
    }
    if (response.status === 200) {
      return response.data || response
    } else {
      // 非200请求报错
      store.dispatch.utils.showToast({text: Locale.getString('common.net_error')})
      throw Error(response.statusText || '服务异常')
    }
  },
  (err) => {
    console.log('请求失败:', err)
    throw Error(err)
  },
)

//格式化打印请求
const logRequest = (config: AxiosRequestConfig, mark: string = '🥎🥎🥎') => {
  try {
    const {common, get, post, put, patch, head, ...other} = config.headers
    delete other.delete
    let output = `\n${mark}\n${config.baseURL + config.url}\nparam:${JSON.stringify(
      config.data,
    )}\nheader:${JSON.stringify(other)}\nmethod:${config.method}\n`
    return output
  } catch (error) {
    console.log('🥎🥎🥎', config)
    return
  }
}

//格式化打印响应
const logResponse = (response: AxiosResponse) => {
  try {
    let mark = response.status === 200 && response.data.code === 200 ? '✅✅✅' : '❌❌❌'
    let configOutput = logRequest(response.config, mark)
    let responseOutput = JSON.stringify(response.data)
    return `${configOutput}response:${responseOutput}\n`
  } catch (error) {
    console.log('❌❌❌', response)
  }
}

class http {
  static async get(url: string, params: object = {}): Promise<any> {
    try {
      let res = null
      if (Object.keys(params).length === 0) {
        res = await instance.get(url)
      } else {
        params = this.filterParams(params)
        params = this._checkSign(url, params)
        const query = this.toParamStr(params)
        res = await instance.get(url + '?' + query)
      }
      return res
    } catch (error) {
      return error
    }
  }
  static async post(
    url: string,
    params: any = {},
    config: AxiosRequestConfig = {},
    useFormData: boolean = false,
  ): Promise<any> {
    try {
      params = this.filterParams(params)
      params = this._checkSign(url, params)

      if (useFormData) {
        let newParams = new FormData()
        for (var key in params) {
          const value = params[key]
          newParams.append(key, value)
        }
        params = newParams
      }

      let res = await instance.post(url, params, {
        headers: {...instance.defaults.headers, ...config.headers},
      })
      return res
    } catch (error) {
      return error
    }
  }
  static async patch(url: string, params: object = {}) {
    try {
      let res = await instance.patch(url, params)
      return res
    } catch (error) {
      return error
    }
  }
  static async put(url: string, params: object = {}) {
    try {
      let res = await instance.put(url, params)
      return res
    } catch (error) {
      return error
    }
  }
  static async delete(url: string, params: object = {}) {
    /**
     * params默认为数组
     */
    try {
      let res = await instance.post(url, params)
      return res
    } catch (error) {
      return error
    }
  }

  /**
   * 将参数字典转为query
   * @param params 参数字典
   */
  static toParamStr(params: any): string {
    let paramStr = ''
    Object.keys(params).map((key) => {
      paramStr += `${key}=${params[key]}&`
    })

    paramStr = paramStr.substring(0, paramStr.lastIndexOf('&'))
    return paramStr
  }

  /**
   * 过滤无效参数
   * @param params
   * @returns
   */
  static filterParams(params: any) {
    const newParams: any = {}

    Object.keys(params).map((key) => {
      const value = params[key]
      if (![undefined, null, NaN].includes(value)) {
        newParams[key] = value
      }
    })

    return newParams
  }

  //检查是否需要签名
  static _checkSign = (url: string, params: any) => {
    //需要签名的api数组
    const signList: string[] = []
    if (!signList.includes(url)) {
      return params
    }
    return this.createSecretParam(params)
  }

  //生成签名
  static createSecretParam = (paramList: any[]) => {
    //盐
    const salt = ''
    let result: any = {
      ...paramList,
      timestamp: Math.round(new Date().getTime() / 1000),
    }

    const sortedParamList = Object.keys(result).sort()
    let sortedParam: any = {}
    sortedParamList.map((item) => {
      sortedParam[item] = result[item]
    })
    const str = this.toParamStr(sortedParam)
    const encodeStr = this.encodeString(str)
    const sign = encodeStr + salt
    result.sign = MD5(sign).toString()
    return result
  }

  //编码，适配后端
  static encodeString = (str: string) => {
    let result = encodeURIComponent(str)
    result = result.replace(/%20/g, '+')
    result = result.replace(/!/g, '%21')
    result = result.replace(/\*/g, '%2A')
    result = result.replace(/\(/g, '%28')
    result = result.replace(/\)/g, '%29')
    result = result.replace(/'/g, '%27')
    result = result.replace(/~/g, '%7E')
    return result
  }
}

export default http

export interface Host {
  title: string
  host: string
}

export class HostDao {
  static HOST_KEY = 'HOST_KEY'
  static currentHost: Host

  static async init() {
    const res = await this.get()
    instance.defaults.baseURL = res.host
    await this.set(res)
  }

  static async get() {
    try {
      if (this.currentHost) {
        return this.currentHost
      }
      const res = JSON.parse((await AsyncStorage.getItem(this.HOST_KEY)) ?? '') as Host
      if (res && this.getList().some((item) => item.host === res.host)) {
        this.currentHost = res
      } else {
        this.currentHost = this.getList()[0]
      }
      return this.currentHost
    } catch (error) {
      this.currentHost = this.getList()[0]
      return this.currentHost
    }
  }

  static set(host: Host) {
    this.currentHost = host
    instance.defaults.baseURL = this.currentHost?.host
    AsyncStorage.setItem(this.HOST_KEY, JSON.stringify(host))
  }

  static getList(): Host[] {
    return [{title: Locale.getString('login.line', {num: '1'}), host: Config.API_HOST}].concat(
      __DEV__ ? [{title: '测试服', host: Config.API_HOST_test}] : [],
    )
  }

  static getCurrentHostName() {
    try {
      return this.getList().filter((item) => item.host === this.currentHost?.host)[0]?.title
    } catch (error) {
      return this.currentHost?.title ?? ''
    }
  }
}
